package com.scarecrow.olga.util.display {
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.InteractiveObject;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.geom.Point;
	
	/**
	 * This is a utility class containing methods for working with displayObjects and containers.
	 * ...
	 * @author Santiago.J.Elizalde
	 */
	public class DisplayUtil {
		
		public function DisplayUtil() {
			
		}
		
		/**
		 * Removes the supplied object from its parent. Does not fail if the object has no parent.
		 * 
		 * @param	display the object to remove.
		 */
		public static function remove(display:DisplayObject):void {
			
			if (!display || !display.parent) return;
			
			display.parent.removeChild(display);
		}
		
		/**
		 * Removes all children from the supplied display object container.
		 * 
		 * @param	container the container to empty.
		 */
		public static function empty(container:DisplayObjectContainer):void {
			
			if (!container) return;
			
			while (container.numChildren) {
				container.removeChildAt(0);
			}
		}
		
		/**
		 * Enables button mode for a sprite. This function will also disable mouse children and set
		 * the useHandCursor property from the supplied Sprite.
		 * 
		 * @param	object the sprite to turn into a button
		 * @param	useHandCursor true if you want the button to use the hand cursor.
		 */
		public static function setAsButton(object:Sprite, useHandCursor:Boolean = true):void {
			if (!object) return;
			
			object.mouseChildren = false;
			object.buttonMode = true;
			object.useHandCursor = useHandCursor;
		}
		
		/**
		 * Places the supplied displayObject on top of its display list, appearing in front of everything.
		 * 
		 * @param	object the object to bring to front.
		 */
		public static function bringToFront(object:DisplayObject):void {
			
			if (!object || !object.parent) return;
			
			var parent:DisplayObjectContainer = object.parent;
			parent.setChildIndex(object, parent.numChildren-1);
		}
		
		/**
		 * Places the supplied displayObject at the bottom of its display list, appearing behind everything.
		 * 
		 * @param	object the object to bring to front.
		 */
		public static function sendToBack(object:DisplayObject):void {
			
			if (!object || !object.parent) return;
			
			var parent:DisplayObjectContainer = object.parent;
			parent.setChildIndex(object, 0);
		}
		
		/**
		 * Sets the mouse interaction for the supplied object and its children (if any).
		 * 
		 * @param	object	the object to enable/disable.
		 * @param	enabled	tells the method to either enable(true) or disable(false) mouse interaction.
		 */
		public static function setMouseEnabled(object:DisplayObject, enabled:Boolean = true):void {
			
			if (!object) return;
			
			if (object is InteractiveObject) {
				InteractiveObject(object).mouseEnabled = enabled;
				
				if (object is DisplayObjectContainer) {
					DisplayObjectContainer(object).mouseChildren = enabled;
				}
			}
		}
		
		/**
		 * Disables mouse interaction with the supplied object and its children(if any).
		 * 
		 * @param	object the display object pn which you want to disable mouse capabilities.
		 */
		public static function disableMouse(object:DisplayObject):void {
			setMouseEnabled(object, false);
		}
		
		/**
		 * Places a display object into another display list, keeping its position relative to the stage.
		 * 
		 * @param	display the display object to relocate.
		 * @param	newParent the container to which the display will be relocated.
		 */
		public static function reparentTo(display:DisplayObject, newParent:DisplayObjectContainer):void {
			
			if (!display || !display.parent || !newParent) return;
			
			var oldParent:DisplayObjectContainer = display.parent;
			var newPos:Point = new Point(display.x, display.y);
			
			newPos = newParent.globalToLocal(oldParent.localToGlobal(newPos));
			
			newParent.addChild(display);
			display.x = newPos.x;
			display.y = newPos.y;
		}
		
		/**
		 * Returns a child object from the supplied container using the provided path.
		 * the path parameter format must be a dot separated path
		 * 
		 * @example "container.container_1.movie_1.myButton"
		 * 
		 * @param	container the container in which the child resides
		 * @param	path	the path (dot-separated string) to the child object
		 * @return	the desired displayObject or null if it was not found.
		 */
		public static function getChildByPath(container:DisplayObjectContainer, path:String):DisplayObject {
			
			var obj:DisplayObject = container;
			for each (var name:String in path.split('.')) {
				if (obj is DisplayObjectContainer === false) return null;
				
				obj = DisplayObjectContainer(obj).getChildByName(name);
				if (!obj) break;
			}
			return obj;
		}
		
		/**
		 * Searches the supplied container for the first ocurrence of a displayObject and returns it.
		 * This function catches SecurityErrors thrown when accessing objects on different sandboxes.
		 * 
		 * WARNING: This function may take long to execute in large displayLists, try to narrow down 
		 * 			the search by providing the nearest possible parent to the object you need.
		 * 
		 * @param	container 	the container in which the child resides.
		 * @param	childName	the name of the child you seek.
		 * @return	the first occurrence of a child with the supplied name, or null if not found.
		 */
		public static function findChild(container:DisplayObjectContainer, childName:String):DisplayObject {
			
			var index:uint = 0;
			var child:DisplayObject = null;
			//If the searched element is a direct child of the container, return it and stop searching.
			child = container.getChildByName(childName);
			if (child) return child;
			
			//then search the container's direct children for the searched element.
			//This is done to improve method performance, it doesn't search recursively if the child is "shallow".
			while (!child && index < container.numChildren) {
				try {
					var childSearchedObject:DisplayObject = container.getChildAt(index);
				} catch(err:SecurityError) {
					// Ignore this item, belongs to another sandbox (like a loaded image or SWF)
				}
				if(childSearchedObject && childSearchedObject is DisplayObjectContainer) {
					child = DisplayObjectContainer(childSearchedObject).getChildByName(childName);
				}
				index++;
			}
			if (!child) {
				//Finally, recurse into any DisplayObjectContainers inside the container itself until found.
				for (var i:int = 0; i < container.numChildren; i++) {
					try {
						var childObject:DisplayObject = container.getChildAt(i);
					} catch(err:SecurityError) {
						// Ignore this item, belongs to another sandbox (like a loaded image or SWF)
					}
					if (childObject && childObject is DisplayObjectContainer) {
						child = findChild(childObject as DisplayObjectContainer, childName);
					}
				}
			}
			return child;
		}
		
		/**
		 * Searches the supplied container for all the ocurrences of a child name and returns a list of them.
		 * This function catches SecurityErrors thrown when accessing objects on different sandboxes.
		 * 
		 * WARNING: This function may take long to execute in large displayLists, try to narrow down 
		 * 			the search by providing the nearest possible parent to the objects you need.
		 * 
		 * @param	container 	the container in which the children will be searched.
		 * @param	childName	the name of the children you seek.
		 * @return	a list with all occurrences of children with the supplied name.
		 */
		public static function findAllChildrenByName(container:DisplayObjectContainer, childName:String):Array {
			
			var list:Array = [];
			
			if(container.getChildByName(childName)) list.push(container.getChildByName(childName));
			
			for (var i:int = 0; i < container.numChildren; i++) {
				try {
					var childObject:DisplayObject = container.getChildAt(i);
				} catch(err:SecurityError) {
					// Ignore this item, belongs to another sandbox (like a loaded image or SWF)
				}
				if (childObject && childObject is DisplayObjectContainer) {
					list = list.concat(findAllChildrenByName(childObject as DisplayObjectContainer, childName));
				}
			}
			return list;
		}
		
		/**
		 * Returns an array with the element's children or descendants (if recursive)
		 * This function catches SecurityErrors thrown when accessing objects on different sandboxes.
		 * 
		 * @param elem The element.
		 * @param recursive If true, the function continues recursively (first children then parents).
		 * @return A regular array with the children.
		 */
		public static function getAllChildren(elem:DisplayObjectContainer, recursive:Boolean=false):Array {
			var list:Array = [];
			for (var i:int = 0, l:int = elem.numChildren; i < l; i++) {
				try {
					var child:DisplayObject = elem.getChildAt(i);
				} catch (err:SecurityError){
					// Ignore this item, belongs to another sandbox (like a loaded image or SWF)
				}
				
				// This is one is odd but happens every now and then, when Flash didn't initialize the item yet
				// Also counts if the catch was met
				if (!child)	continue;
				
				if (recursive && child is DisplayObjectContainer) {
					for each (var descendant:DisplayObject in getAllChildren(child as DisplayObjectContainer, true)) {
						list.push(descendant);
					}
				}
				
				list.push(child);
			}
			
			return list;
		}
	}
}